package day27.Thread;

/**
 * 多线程测试主类
 * 演示了Java中创建和使用线程的多种方式，包括继承Thread类和实现Runnable接口
 * 同时演示了线程同步的概念
 */
public class ThreadTest {
    /**
     * 程序入口点
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 测试1: 通过继承Thread类创建线程
        // 创建两个自定义线程实例
        // Thread类是Java中线程的基本实现类，每个Thread实例代表一个独立的执行线程
        MyThread thread1 = new MyThread("Thread-1");
        MyThread thread2 = new MyThread("Thread-2");
        
        // 测试2: 通过实现Runnable接口创建线程
        // 创建Runnable任务实例
        // Runnable接口是另一种创建线程的方式，更符合面向对象的设计原则
        // 因为Java不支持多继承，实现接口可以避免继承限制
        MyRunnable runnable1 = new MyRunnable("Runnable-1");
        MyRunnable runnable2 = new MyRunnable("Runnable-2");
        // 将Runnable任务包装成Thread对象
        // Thread构造函数可以接受Runnable实例，这样线程执行时会调用Runnable的run方法
        Thread thread3 = new Thread(runnable1);
        Thread thread4 = new Thread(runnable2);
        
        System.out.println("开始执行线程测试...");
        
        // 启动所有线程，调用start()方法会使JVM调用线程的run()方法
        // start()方法会创建新的执行线程，而直接调用run()方法只是普通的方法调用
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        
        // 等待所有线程执行完毕，join()方法会使当前线程等待调用join()的线程执行完成
        // join()方法确保主线程等待所有子线程执行完毕后再继续执行
        try {
            thread1.join();
            thread2.join();
            thread3.join();
            thread4.join();
        } catch (InterruptedException e) {
            // InterruptedException在当前线程被中断时抛出
            e.printStackTrace();
        }
        
        System.out.println("所有线程执行完毕");
        
        // 测试3: 演示线程同步
        System.out.println("\n开始线程同步测试...");
        // 创建共享资源对象
        // 在多线程环境中，多个线程可能同时访问同一个资源，需要同步机制保证数据一致性
        SharedResource resource = new SharedResource();
        
        // 创建两个使用相同共享资源的线程
        // 通过Runnable接口创建线程，同时指定线程名称
        Thread syncThread1 = new Thread(new SynchronizedRunnable(resource, "Sync-1"), "SyncThread-1");
        Thread syncThread2 = new Thread(new SynchronizedRunnable(resource, "Sync-2"), "SyncThread-2");
        
        // 启动同步测试线程
        syncThread1.start();
        syncThread2.start();
        
        // 等待同步测试线程执行完毕
        try {
            syncThread1.join();
            syncThread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("线程同步测试结束");
    }
}

/**
 * 通过继承Thread类创建线程的示例
 * 自定义线程类，继承自Thread类
 * Thread类实现了Runnable接口，所以重写run方法是必要的
 */
class MyThread extends Thread {
    private String name; // 线程名称
    
    /**
     * 构造方法，初始化线程名称
     * @param name 线程名称
     */
    public MyThread(String name) {
        // 调用父类Thread的构造方法设置线程名称
        super(name);
        this.name = name;
    }
    
    /**
     * 重写run方法，定义线程执行的逻辑
     * 每个线程会循环5次，每次输出计数并暂停100毫秒
     * run方法包含了线程执行的具体任务
     */
    @Override
    public void run() {
        // 循环5次，模拟线程执行的任务
        for (int i = 1; i <= 5; i++) {
            // 输出当前线程名称和计数值
            // 多个线程会交替执行，输出顺序可能不固定
            System.out.println(name + " - 计数: " + i);
            try {
                // 暂停线程一段时间，模拟实际处理过程
                // Thread.sleep()会使当前线程进入阻塞状态指定的毫秒数
                // 这会释放CPU资源给其他线程执行
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // 当线程在sleep期间被中断时会抛出InterruptedException
                // 这通常发生在其他线程调用了当前线程的interrupt()方法
                e.printStackTrace();
            }
        }
        // 线程执行完毕后的提示信息
        System.out.println(name + " 执行完毕");
    }
}

/**
 * 通过实现Runnable接口创建线程的示例
 * 实现Runnable接口是创建线程的另一种方式，相比继承Thread类更加灵活
 * Runnable接口只定义了一个run方法，需要实现该方法定义线程执行逻辑
 */
class MyRunnable implements Runnable {
    private String name; // 任务名称
    
    /**
     * 构造方法，初始化任务名称
     * @param name 任务名称
     */
    public MyRunnable(String name) {
        this.name = name;
    }
    
    /**
     * 实现run方法，定义线程执行的逻辑
     * 每个任务会循环5次，每次输出计数并暂停150毫秒
     * Runnable的run方法与Thread类的run方法作用相同
     */
    @Override
    public void run() {
        // 循环5次执行任务，每次间隔时间比MyThread稍长
        for (int i = 1; i <= 5; i++) {
            // 输出当前任务名称和计数值
            System.out.println(name + " - 计数: " + i);
            try {
                // 暂停线程一段时间，时间比MyThread稍长，便于观察执行顺序
                // 不同的暂停时间可以让我们更清楚地看到多线程的并发执行效果
                Thread.sleep(150);
            } catch (InterruptedException e) {
                // 处理线程中断异常
                // 当线程在sleep状态时被其他线程中断会抛出此异常
                e.printStackTrace();
            }
        }
        // 任务执行完毕后的提示信息
        System.out.println(name + " 执行完毕");
    }
}

/**
 * 共享资源类，用于演示线程同步
 * 包含一个计数器变量，在多线程环境下需要同步访问
 * 如果不进行同步，多个线程同时修改共享变量可能导致数据不一致
 */
class SharedResource {
    private int count = 0; // 共享计数变量，多个线程将同时访问这个变量
    
    /**
     * 同步方法，用于增加计数器的值
     * 使用synchronized关键字确保同一时间只有一个线程可以执行此方法
     * synchronized关键字可以保证方法级别的线程安全
     * @param threadName 调用此方法的线程名称
     */
    public synchronized void increment(String threadName) {
        // 读取当前count值
        // 在没有同步的情况下，这一步可能在多个线程间产生竞态条件
        int temp = count;
        System.out.println(threadName + " 读取到count值: " + temp);
        
        try {
            // 模拟一些处理时间，增加出现并发问题的可能性
            // 在实际的复杂操作中，这种延迟很常见，会增加并发问题的风险
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // 处理线程中断异常
            e.printStackTrace();
        }
        
        // 更新count值
        // 如果没有同步机制，这一步在多线程环境下可能出现问题
        // 例如两个线程同时读取到相同的count值，然后都加1，最终结果会丢失一次增加操作
        count = temp + 1;
        System.out.println(threadName + " 将count更新为: " + count);
    }
}

/**
 * 用于演示同步的Runnable实现
 * 每个实例都持有对共享资源的引用，并在run方法中调用共享资源的方法
 * 这个类展示了如何在多线程环境中安全地使用共享资源
 */
class SynchronizedRunnable implements Runnable {
    private SharedResource resource; // 共享资源引用
    private String name; // 任务名称
    
    /**
     * 构造方法，初始化共享资源和任务名称
     * @param resource 共享资源对象
     * @param name 任务名称
     */
    public SynchronizedRunnable(SharedResource resource, String name) {
        this.resource = resource;
        this.name = name;
    }
    
    /**
     * 实现run方法，循环3次调用共享资源的increment方法
     * 展示了如何在多线程环境中正确使用共享资源
     */
    @Override
    public void run() {
        // 循环3次调用共享资源的方法
        for (int i = 1; i <= 3; i++) {
            // 调用共享资源的同步方法
            // 由于increment方法使用了synchronized关键字，所以同一时间只有一个线程可以执行
            resource.increment(name);
            try {
                // 每次调用后暂停一段时间
                // 暂停可以让其他线程有机会获得执行权
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // 处理线程中断异常
                e.printStackTrace();
            }
        }
        // 输出线程执行完毕信息，Thread.currentThread().getName()获取当前线程名称
        // 这个方法可以获取当前正在执行的线程对象的名称
        System.out.println(Thread.currentThread().getName() + " 执行完毕");
    }
}